-- This file is part of the Wire Server implementation.
--
-- Copyright (C) 2022 Wire Swiss GmbH <opensource@wire.com>
--
-- This program is free software: you can redistribute it and/or modify it under
-- the terms of the GNU Affero General Public License as published by the Free
-- Software Foundation, either version 3 of the License, or (at your option) any
-- later version.
--
-- This program is distributed in the hope that it will be useful, but WITHOUT
-- ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
-- FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
-- details.
--
-- You should have received a copy of the GNU Affero General Public License along
-- with this program. If not, see <https://www.gnu.org/licenses/>.

module Galley.API.LegalHold.Team
  ( isLegalHoldEnabledForTeam,
    computeLegalHoldFeatureStatus,
    assertLegalHoldEnabledForTeam,
    ensureNotTooLargeToActivateLegalHold,
    teamSizeBelowLimit,
  )
where

import Data.Default
import Data.Id
import Data.Range
import Galley.Effects
import Galley.Effects.TeamFeatureStore
import Galley.Env
import Galley.Types.Teams as Team
import Imports
import Polysemy
import Polysemy.Input (Input, input)
import Wire.API.Error
import Wire.API.Error.Galley
import Wire.API.Team.Feature
import Wire.API.Team.Size
import Wire.BrigAPIAccess
import Wire.LegalHoldStore qualified as LegalHoldData

assertLegalHoldEnabledForTeam ::
  forall r.
  ( Member LegalHoldStore r,
    Member TeamFeatureStore r,
    Member (Input (FeatureDefaults LegalholdConfig)) r,
    Member (ErrorS 'LegalHoldNotEnabled) r
  ) =>
  TeamId ->
  Sem r ()
assertLegalHoldEnabledForTeam tid =
  unlessM (isLegalHoldEnabledForTeam tid) $
    throwS @'LegalHoldNotEnabled

computeLegalHoldFeatureStatus ::
  ( Member LegalHoldStore r,
    Member (Input (FeatureDefaults LegalholdConfig)) r
  ) =>
  TeamId ->
  DbFeature LegalholdConfig ->
  Sem r FeatureStatus
computeLegalHoldFeatureStatus tid dbFeature = do
  featureLegalHold <- input @(FeatureDefaults LegalholdConfig)
  case featureLegalHold of
    FeatureLegalHoldDisabledPermanently -> pure FeatureStatusDisabled
    FeatureLegalHoldDisabledByDefault ->
      pure (applyDbFeature dbFeature def).status
    FeatureLegalHoldWhitelistTeamsAndImplicitConsent -> do
      wl <- LegalHoldData.isTeamLegalholdWhitelisted tid
      pure $ if wl then FeatureStatusEnabled else FeatureStatusDisabled

isLegalHoldEnabledForTeam ::
  forall r.
  ( Member LegalHoldStore r,
    Member TeamFeatureStore r,
    Member (Input (FeatureDefaults LegalholdConfig)) r
  ) =>
  TeamId ->
  Sem r Bool
isLegalHoldEnabledForTeam tid = do
  dbFeature <- getDbFeature tid
  status <- computeLegalHoldFeatureStatus tid dbFeature
  pure $ status == FeatureStatusEnabled

ensureNotTooLargeToActivateLegalHold ::
  ( Member BrigAPIAccess r,
    Member (ErrorS 'CannotEnableLegalHoldServiceLargeTeam) r,
    Member (Input FanoutLimit) r,
    Member (Input (FeatureDefaults LegalholdConfig)) r
  ) =>
  TeamId ->
  Sem r ()
ensureNotTooLargeToActivateLegalHold tid = do
  (TeamSize teamSize) <- getSize tid
  unlessM (teamSizeBelowLimit (fromIntegral teamSize)) $
    throwS @'CannotEnableLegalHoldServiceLargeTeam

teamSizeBelowLimit ::
  ( Member (Input FanoutLimit) r,
    Member (Input (FeatureDefaults LegalholdConfig)) r
  ) =>
  Int ->
  Sem r Bool
teamSizeBelowLimit teamSize = do
  limit <- fromIntegral . fromRange <$> input @FanoutLimit
  let withinLimit = teamSize <= limit
  featureLegalHold <- input @(FeatureDefaults LegalholdConfig)
  case featureLegalHold of
    FeatureLegalHoldDisabledPermanently -> pure withinLimit
    FeatureLegalHoldDisabledByDefault -> pure withinLimit
    FeatureLegalHoldWhitelistTeamsAndImplicitConsent ->
      -- unlimited, see docs of 'ensureNotTooLargeForLegalHold'
      pure True
